package com.coderknock.algorithm;

import redis.clients.jedis.BitOP;
import redis.clients.jedis.Jedis;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
/**
 * bitmap测试
 *
 * @author mouliu
 * @create 2018-02-10-上午9:48
 */
public class BitMaps {
    @DisplayName("Bitmaps")
    @Test
    //用户签订场景使用
    public void userQd(){
        //链接redis
        Jedis redis = new Jedis("127.0.0.1",6379,100000);
        redis.auth("redis213daianla2016");
        //用户uid
        String uid = "1";
        String cacheKey = "sign_"+Integer.valueOf(uid);
        //记录有uid的key
        // $cacheKey = sprintf("sign_%d", $uid);

        //开始有签到功能的日期
        String startDate = "2017-01-01";

        //今天的日期
        String todayDate = "2017-01-21";

        //计算offset(时间搓)
        long startTime = dateParase(startDate,"yyyy-MM-dd").getTime();
        long todayTime = dateParase(todayDate,"yyyy-MM-dd").getTime();
        long offset = (long) Math.floor((todayTime - startTime) / 86400);

        System.out.println("今天是第"+offset+"天");

        //签到
        //一年一个用户会占用多少空间呢？大约365/8=45.625个字节，好小，有木有被惊呆？
        redis.setbit(cacheKey,offset,"1");

        //查询签到情况
        boolean bitStatus = redis.getbit(cacheKey, offset);
        //判断是否已经签到
        //计算总签到次数
        long qdCount = redis.bitcount(cacheKey);

        /**
         * 计算某段时间内的签到次数
         * 很不幸啊,bitCount虽然提供了start和end参数，但是这个说的是字符串的位置，而不是对应"位"的位置
         * 幸运的是我们可以通过get命令将value取出来，自己解析。并且这个value不会太大，上面计算过一年一个用户只需要45个字节
         * 给我们的网站定一个小目标，运行30年，那么一共需要1.31KB(就问你屌不屌？)
         */
        //这是个错误的计算方式
        //echo $redis->bitCount($cacheKey, 0, 20) . PHP_EOL;
    }
    public Date dateParase(String dateStr, String dateFormartStr){
        SimpleDateFormat dateFormart = new SimpleDateFormat(dateFormartStr);
        try {
            return dateFormart.parse(dateStr);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }
    @DisplayName("Bitmaps")
    @Test
    //用户签订场景使用
    public void activeUser(){
        Map<String,List<Integer>>dateActiveuser = new HashMap<>();
        Jedis redis = new Jedis("127.0.0.1",6379,100000);
        redis.auth("redis213daianla2016");
        Integer[] temp01 = {1,2,3,4,5,6,7,8,9,10};
        List<Integer>temp01List = new ArrayList<>();
        Collections.addAll(temp01List,temp01);
        dateActiveuser.put("2017-01-10",temp01List);


        Integer[] temp02 = {1,2,3,4,5,6,7,8};
        List<Integer>temp02List = new ArrayList<>();
        Collections.addAll(temp02List,temp02);
        dateActiveuser.put("2017-01-11",temp02List);

        Integer[] temp03 = {1,2,3,4,5,6};
        List<Integer>temp03List = new ArrayList<>();
        Collections.addAll(temp03List,temp03);
        dateActiveuser.put("2017-01-12",temp03List);

        Integer[] temp04 = {1,4,5,6};
        List<Integer>temp04List = new ArrayList<>();
        Collections.addAll(temp04List,temp04);
        dateActiveuser.put("2017-01-13",temp04List);

        Integer[] temp05 = {1,4,5,6};
        List<Integer>temp05List = new ArrayList<>();
        Collections.addAll(temp05List,temp05);
        dateActiveuser.put("2017-01-14",temp05List);

        String date[] = {"2017-01-10","2017-01-11","2017-01-12","2017-01-13","2017-01-14"};

        //测试数据放入redis中
        for (int i=0;i<date.length;i++){
            for (int j=0;j<dateActiveuser.get(date[i]).size();j++){
                redis.setbit(date[i], dateActiveuser.get(date[i]).get(j), "1");
            }
        }

        //bitOp
        redis.bitop(BitOP.AND, "stat", "2017-01-10", "2017-01-11","2017-01-12");

        System.out.println("总活跃用户："+redis.bitcount("stat"));

        redis.bitop(BitOP.AND, "stat1", "2017-01-10", "2017-01-11","2017-01-14");
        System.out.println("总活跃用户："+redis.bitcount("stat1"));

        redis.bitop(BitOP.AND, "stat2", "2017-01-10", "2017-01-11");
        System.out.println("总活跃用户："+redis.bitcount("stat2"));
    }
    /**
     * 使用场景三：用户在线状态
         前段时间开发一个项目，对方给我提供了一个查询当前用户是否在线的接口。不了解对方是怎么做的，自己考虑了一下，
        使用bitmap是一个节约空间效率又高的一种方法，只需要一个key，然后用户ID为offset，如果在线就设置为1，不在线就设置为0，和上面的场景一样，5000W用户只需要6MB的空间。

         //批量设置在线状态
         $uids = range(1, 500000); 
         foreach($uids as $uid) { 
             $redis->setBit('online', $uid, $uid % 2);
         } 
         //一个一个获取状态
          $uids = range(1, 500000); 
         $startTime = microtime(true); 
         foreach($uids as $uid) { 
            echo $redis->getBit('online', $uid) . PHP_EOL;
         }
          $endTime = microtime(true); 
         //在我的电脑上，获取50W个用户的状态需要25秒 
         echo "total:" . ($endTime - $startTime) . "s";

        /** 
     * 对于批量的获取，上面是一种效率低的办法，实际可以通过get获取到value，然后自己计算 
     * 具体计算方法改天再写吧，之前写的代码找不见了。。。 
     */
    @Test
    public void actionUserOnLine(){
        Jedis redis = new Jedis("127.0.0.1",6379,100000);
        redis.auth("redis213daianla2016");

        Integer[] uids = new Integer[500000];
        //创建一个数值，并赋值
        for (int i=0;i<500000;i++){
            uids[i]= i;
        }
        for(Integer uid:uids){
            String uidValue = String.valueOf(uid%2);
            redis.setbit("online",uid,uidValue);
        }
        //一个一个的获取
        Long startCurrTime = System.currentTimeMillis();
        for (Integer uid:uids){
            System.out.println(redis.getbit("online",uid));
        }
        Long endCurrTime = System.currentTimeMillis();
        System.out.println("$endTime - $startTime"+(endCurrTime-startCurrTime));
    }
}
